
package com.huaweicloud.saaslanding.install.applicationservice.impl;

import com.huaweicloud.saaslanding.basic.constant.EnvConstant;
import com.huaweicloud.saaslanding.basic.constant.UrlConstant;
import com.huaweicloud.saaslanding.basic.enumeration.CceServicePrefixEnum;
import com.huaweicloud.saaslanding.basic.enumeration.ComponentLabelEnum;
import com.huaweicloud.saaslanding.basic.enumeration.RedisServerTypeEnum;
import com.huaweicloud.saaslanding.basic.enumeration.SecretNameEnum;
import com.huaweicloud.saaslanding.basic.enumeration.ServerType;
import com.huaweicloud.saaslanding.basic.enumeration.ServiceManageFrameTypeEnum;
import com.huaweicloud.saaslanding.basic.enumeration.deployment.KeyEnum;
import com.huaweicloud.saaslanding.basic.enumeration.deployment.SslStoreEnum;
import com.huaweicloud.saaslanding.basic.exception.BizException;
import com.huaweicloud.saaslanding.basic.exception.code.ExceptionCode;
import com.huaweicloud.saaslanding.basic.routelink.constant.RouteLinkConstant;
import com.huaweicloud.saaslanding.basic.utils.EncryptionUtil;
import com.huaweicloud.saaslanding.basic.utils.StringFormatConversion;
import com.huaweicloud.saaslanding.basic.utils.ThreadPoolManager;
import com.huaweicloud.saaslanding.dao.common.entity.app.AppTemplate;
import com.huaweicloud.saaslanding.dao.common.entity.deployedresource.CceDeploymentResource;
import com.huaweicloud.saaslanding.dao.common.entity.deployedresource.CceDeploymentResourceRelation;
import com.huaweicloud.saaslanding.dao.common.entity.deployedresource.CceResource;
import com.huaweicloud.saaslanding.dao.common.entity.deployedresource.CceServiceResource;
import com.huaweicloud.saaslanding.dao.common.repository.app.AppTemplateBaseMapper;
import com.huaweicloud.saaslanding.dao.common.repository.deployedresource.CceDeploymentResourceBaseMapper;
import com.huaweicloud.saaslanding.dao.common.repository.deployedresource.CceDeploymentResourceRelationBaseMapper;
import com.huaweicloud.saaslanding.dao.common.repository.deployedresource.CceResourceBaseMapper;
import com.huaweicloud.saaslanding.dao.common.repository.deployedresource.CceServiceResourceBaseMapper;
import com.huaweicloud.saaslanding.dao.common.repository.tenant.TenantBaseMapper;
import com.huaweicloud.saaslanding.huawei.cce.api.CceConfigMapApi;
import com.huaweicloud.saaslanding.huawei.cce.api.CceDeploymentApi;
import com.huaweicloud.saaslanding.huawei.cce.api.CceServiceApi;
import com.huaweicloud.saaslanding.huawei.cce.api.NginxIngressApi;
import com.huaweicloud.saaslanding.huawei.cce.dto.CceDeploymentDto;
import com.huaweicloud.saaslanding.huawei.cce.dto.CceNamespaceDto;
import com.huaweicloud.saaslanding.huawei.cce.dto.CceSecretDto;
import com.huaweicloud.saaslanding.huawei.cce.model.ContainerEnv;
import com.huaweicloud.saaslanding.huawei.cce.model.ingress.IngressCreatDTO;
import com.huaweicloud.saaslanding.huawei.cce.request.CceConfigMapCreateRequest;
import com.huaweicloud.saaslanding.huawei.cce.request.CceDeploymentCreateRequest;
import com.huaweicloud.saaslanding.huawei.cce.request.CceServiceCreateRequest;
import com.huaweicloud.saaslanding.huawei.cce.request.IngressInfo;
import com.huaweicloud.saaslanding.huawei.cce.response.CceDeploymentResponse;
import com.huaweicloud.saaslanding.huawei.cce.response.CceServiceResponse;
import com.huaweicloud.saaslanding.huawei.cce.util.CceDeploymentUtil;
import com.huaweicloud.saaslanding.huawei.cce.util.CceHeaderUtil;
import com.huaweicloud.saaslanding.huawei.cce.util.CceNamespaceUtil;
import com.huaweicloud.saaslanding.huawei.cce.util.CceRequestBodyUtil;
import com.huaweicloud.saaslanding.huawei.cce.util.CceSecretUtil;
import com.huaweicloud.saaslanding.huawei.cce.util.IngressRequestBody;
import com.huaweicloud.saaslanding.huawei.dcs.RedisUtils;
import com.huaweicloud.saaslanding.huawei.dcs.keyenum.RedisKeyEnum;
import com.huaweicloud.saaslanding.install.applicationservice.CceService;
import com.huaweicloud.saaslanding.install.dto.CceDto;
import com.huaweicloud.saaslanding.install.dto.SaltDto;
import com.huaweicloud.saaslanding.install.json.CceRequestBody;
import com.huaweicloud.saaslanding.install.properties.Cce;
import com.huaweicloud.saaslanding.install.properties.Cse;
import com.huaweicloud.saaslanding.install.properties.Dcs;
import com.huaweicloud.saaslanding.install.properties.Endpoint;
import com.huaweicloud.saaslanding.install.properties.Iam;
import com.huaweicloud.saaslanding.install.properties.Nacos;
import com.huaweicloud.saaslanding.install.properties.Skywalking;
import com.huaweicloud.saaslanding.install.properties.Ssl;
import com.huaweicloud.saaslanding.install.repository.ComponentInfoMapper;
import com.huaweicloud.saaslanding.install.repository.WorkSecretKeyMapper;
import com.huaweicloud.saaslanding.install.util.DomainNameUtil;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.StrUtil;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

/**
 * 功能描述
 *
 * @since 2022-07-29
 */
@Slf4j
@Component
public class CceServiceImpl implements CceService {

    /**
     * 初始化线程池
     */
    ThreadPoolExecutor poolExecutor = ThreadPoolManager.newInstance();

    private Map<String, String> headers;

    @Value("${saaslanding-install.swagger.enabled:false}")
    private Boolean swaggerEnabled;

    @Resource
    private AppTemplateBaseMapper appTemplateBaseMapper;

    @Resource
    private CceDeploymentResourceRelationBaseMapper cceDeploymentResourceRelationBaseMapper;

    @Resource
    private TenantBaseMapper tenantBaseMapper;

    @Resource
    private CceResourceBaseMapper cceResourceBaseMapper;

    @Resource
    private WorkSecretKeyMapper workSecretKeyMapper;

    @Resource
    private ComponentInfoMapper componentInfoMapper;

    @Resource
    private CceServiceResourceBaseMapper cceServiceResourceBaseMapper;

    @Resource
    private CceDeploymentResourceBaseMapper cceDeploymentResourceBaseMapper;

    @Resource
    private Cce cce;

    @Resource
    private Iam iam;

    @Resource
    private Cse cse;

    @Resource
    private Nacos nacos;

    @Resource
    private Ssl ssl;

    @Resource
    Endpoint endpoint;

    @Resource
    private RedisUtils redisUtils;

    @Resource
    private Dcs dcs;

    @Value("${saaslanding-install.config-discovery-frame.type}")
    private String CONFIG_DISCOVERY_FRAME;

    @Value("${spring.profiles.active}")
    private String ACTIVE;

    @Value("${saaslanding-install.ingress.ingressClassName}")
    String ingressClassName;

    @Resource
    private Skywalking skywalking;

    /**
     * 创建cce namespace
     */
    @Override
    public void createNamespace() {
        log.info("========== start: 创建cce namespace ==========");

        // 获取平台侧租户code作为namespace名
        String namespace = tenantBaseMapper.getIsvTenantCode();

        CceNamespaceDto namespaceDto = new CceNamespaceDto();
        namespaceDto.setName(namespace)
            .setHeaders(getHeaders())
            .setEndpoint(endpoint.getCce())
            .setClusterId(cce.getClusterId());
        // 判断 namespace 是否创建
        if (CceNamespaceUtil.existed(namespaceDto)) {
            // 已有资源会影响新资源，最好删掉再创
            log.info("namespace {} 已创建，无需再次进行创建操作", namespace);
        } else {
            CceNamespaceUtil.create(namespaceDto);
            log.info("创建namespace {} 成功", namespace);
        }

        log.info("========== end: 创建cce namespace ==========");
    }

    @Override
    public void deleteNamespace() {
        log.info("========== start: 删除cce namespace ==========");

        List<String> toDeleteList = cceResourceBaseMapper.findNamespace();
        // 过滤掉空命名空间
        Set<String> toDeleteSet = toDeleteList.stream().filter(StringUtils::isNotBlank).collect(Collectors.toSet());
        String isvTenantCode = tenantBaseMapper.getIsvTenantCode();
        if (StrUtil.isNotEmpty(isvTenantCode)) {
            toDeleteSet.add(isvTenantCode);
        }
        log.info("需要删除的cce namespace：{}", toDeleteSet);

        CceNamespaceDto namespaceDto = new CceNamespaceDto();
        namespaceDto.setHeaders(getHeaders()).setEndpoint(endpoint.getCce()).setClusterId(cce.getClusterId());
        for (String name : toDeleteSet) {
            namespaceDto.setName(name);
            // 判断namespace是否创建
            if (!CceNamespaceUtil.existed(namespaceDto)) {
                log.info("namespace {} 不存在，无需进行删除操作", name);
            } else {
                // 删除ingress
                deleteIngress(name);

                Boolean deleted = CceNamespaceUtil.delete(namespaceDto);
                if (deleted) {
                    log.info("删除namespace {} 成功", name);
                } else {
                    log.error("!!!!!!!!!! 删除namespace {} 失败 !!!!!!!!!!", name);
                }
            }
        }

        log.info("========== end: 删除cce namespace ==========");
    }

    /**
     * 删除命名空间下所有ingress
     */
    private void deleteIngress(String namespace) {
        NginxIngressApi.deleteAll(
            cce.getClusterId(), endpoint.getCce(), namespace, CceHeaderUtil.getHeaders(iam.getEndpoint(),
                iam.getIamDomain(), iam.getIamUser(), iam.getIamPassword(), iam.getProjectId()),
            cce.getClusterVersion());
    }

    @Override
    public void createSecrets() {
        log.info("========== start: 创建cce secret ==========");

        // 根据SSL是否开启的配置，判断是否需要进行证书相关的Secret创建
        if (!ssl.isEnabled()) {
            log.info("未开启SSL认证，无需创建证书相关Secret");
        }

        // 获取namespace，平台侧租户Code即为namespace名
        String namespace = tenantBaseMapper.getIsvTenantCode();

        // 根据SSL是否开启的配置，判断是否需要进行证书相关的Secret创建
        if (ssl.isEnabled()) {
            // 创建云服务https认证密钥
            createCloudServiceSecret(namespace);
        } else {
            log.info("未开启SSL认证，无需创建证书相关Secret");
        }

        // 获取根秘钥及工作秘钥，并进行Base64位编码
        SaltDto saltDto = workSecretKeyMapper.getPlatformSaltDto();
        String rootKey = Base64.encode(saltDto.getPwdSalt());
        String encryptedWorkPw = Base64.encode(saltDto.getPassword());

        // 创建根秘钥及工作秘钥Secret
        createSaltKeySecret(namespace, rootKey, encryptedWorkPw);
    }

    /**
     * 创建云服务https认证密钥
     *
     * @param namespaceName
     *        命名空间名称
     */
    private void createCloudServiceSecret(String namespaceName) {
        Map<String, String> data = new HashMap<>();
        data.put(SslStoreEnum.KEY_STORE.getKey(), ssl.getKeyStore());
        data.put(SslStoreEnum.TRUST_STORE.getKey(), ssl.getTrustStore());

        CceSecretDto secretDto = new CceSecretDto();
        secretDto.setName(SecretNameEnum.CLOUD_SERVICE.getName())
            .setNamespaceName(namespaceName)
            .setData(data)
            .setEndpoint(endpoint.getCce())
            .setClusterId(cce.getClusterId())
            .setHeaders(getHeaders());

        CceSecretUtil.create(secretDto);
        log.info("创建cloud service secret {} 成功", SecretNameEnum.CLOUD_SERVICE.getName());
    }

    /**
     * 创建根秘钥及工作秘钥密钥
     *
     * @param namespaceName
     *        命名空间名称
     */
    private void createSaltKeySecret(String namespaceName, String rootKey, String encryptedWorkPw) {
        Map<String, String> data = new HashMap<>();
        data.put(KeyEnum.ROOT_KEY.getKey(), rootKey);
        data.put(KeyEnum.WORK_PW.getKey(), encryptedWorkPw);

        CceSecretDto secretDto = new CceSecretDto();
        secretDto.setName(SecretNameEnum.KEY.getName())
            .setNamespaceName(namespaceName)
            .setData(data)
            .setEndpoint(endpoint.getCce())
            .setClusterId(cce.getClusterId())
            .setHeaders(getHeaders());

        CceSecretUtil.create(secretDto);

        log.info("创建根秘钥及工作秘钥 secret {} 成功", SecretNameEnum.CLOUD_SERVICE.getName());
    }

    @Override
    public List<CceDto> createCceDtoList() {
        // 查询控制台应用 应用模板表id
        Long appId = appTemplateBaseMapper.selectIdByType(1);

        // 查询cce资源部署所需信息
        return componentInfoMapper.getCceDto(appId)
            .stream()
            .filter(cce -> !(ComponentLabelEnum.B_UI.getLabel().equals(cce.getLabel())
                && "0.0.2".equals(cce.getComponentVersion())))
            .collect(Collectors.toList());
    }

    @Override
    public void createCceResource(CceDto cceDto) {
        log.info("========== start: 创建 cce deployment : {} ==========", cceDto.getName());
        // 获取平台侧租户code作为namespace名
        String namespace = tenantBaseMapper.getIsvTenantCode();

        // 获取信息
        Map<String, String> cseInfo = getCseInfo();
        Map<String, String> iamInfo = getIamInfo();

        // 获取平台侧加密口令
        String salt = getIsvSalt();

        // 加密
        String sk = "ENC(" + EncryptionUtil.encrypt(salt, iamInfo.get("secretKey")) + ")";
        String pwd = "ENC(" + EncryptionUtil.encrypt(salt, cseInfo.get("csePwd")) + ")";

        CceDeploymentCreateRequest requestBody;
        Map<String, String> envMap;
        CceDeploymentResponse deploymentResponse;
        if (ComponentLabelEnum.B_UI.getLabel().equals(cceDto.getLabel())) {

            CceDeploymentDto deploymentDto = new CceDeploymentDto();
            deploymentDto.setEndpoint(endpoint.getCce()).setClusterId(cce.getClusterId()).setHeaders(getHeaders());
            deploymentDto.setName(cceDto.getName())
                .setNamespaceName(namespace)
                .setComponentName(ServerType.NEW_UI.getComponentName())
                .setEnv(getUIEnv())
                .setImage(cceDto.getImage())
                .setSslEnabled(ssl.isEnabled());

            deploymentResponse = CceDeploymentUtil.createUI(deploymentDto);
        } else {
            // 构造请求体
            envMap = generateRequestHashMap(cceDto, namespace, cseInfo, iamInfo, sk, pwd);
            requestBody = CceRequestBody.createDeploymentBody(envMap, ssl.isEnabled());
            deploymentResponse =
                CceDeploymentApi.create(endpoint.getCce(), cce.getClusterId(), namespace, requestBody, getHeaders());
        }
        // 解析 创建 cce deployment 的请求结果
        parseCreateDeploymentResult(deploymentResponse, cceDto);
    }

    /**
     * 设置前端环境变量
     *
     * @return 前端环境变量
     */
    private List<ContainerEnv> getUIEnv() {
        List<ContainerEnv> envs = new ArrayList<>();
        if (RedisServerTypeEnum.isValidatedType(dcs.getType())) {
            envs.add(new ContainerEnv(EnvConstant.REDIS_SERVER, dcs.getNodes()));
        } else {
            log.error("Redis连接信息配置错误！！！");
            throw new BizException(ExceptionCode.ILLEGAL_ARGUMENT_EX);
        }
        envs.add(new ContainerEnv(EnvConstant.REDIS_PASSWORD, dcs.getPassword()));
        return envs;
    }

    @Override
    public void initCce() {
        log.info("<-----------------开始初始化Cce相关配置---------------->");
        // 创建nameSpace
        log.info("-----------------开始创建nameSpace-------------------");
        createNamespace();
        log.info("-----------------nameSpace创建成功-------------------");

        // 创建Secret
        log.info("-----------------开始创建CCE Secret-------------------");
        createSecrets();
        log.info("-----------------CCE Secret创建成功-------------------");

        // 创建cce资源
        log.info("-----------------开始创建cce资源-------------------");
        List<CceDto> cceResources = createCceDtoList().stream()
            .filter(cce -> "0.0.1".equals(cce.getComponentVersion()))
            .collect(Collectors.toList());

        createCceResourcesCfs(cceResources);
        log.info("<-----------------Cce相关配置初始化完成----------------->");
    }

    private void createCceResourcesCfs(List<CceDto> cceResources) {
        CompletableFuture<?>[] cfs = cceResources.stream()
            .map(cceResource -> CompletableFuture.runAsync(() -> createCceResource(cceResource), poolExecutor)
                .thenAccept(v -> log.info("---------" + cceResource.getName() + "创建成功!--------"))
                .exceptionally(e -> {
                    // 截取错误信息
                    String message = "---------" + cceResource.getName() + "创建失败--------"
                        + StringFormatConversion.cutString(e.getMessage(), "=", ",");
                    log.error(message);
                    throw new CompletionException(e);
                }))
            .toArray(CompletableFuture[]::new);
        // 等待总任务完成，但是无返回值，必须whenComplete()获取
        CompletableFuture.allOf(cfs).join();
    }

    /**
     * 生成CCE工作负载创建 Map
     *
     * @param cceDto 工作负载基本信息
     * @param namespace 命名空间
     * @param cseInfo CSE信息
     * @param iamInfo IAM信息
     * @param sk SK
     * @param pwd 密码
     */
    private HashMap<String, String> generateRequestHashMap(CceDto cceDto, String namespace, Map<String, String> cseInfo,
        Map<String, String> iamInfo, String sk, String pwd) {

        if (ACTIVE.equals("dev") || ACTIVE.equals("product") || ACTIVE.equals("wulandev")) {
            log.error("-------------检查配置文件active字段是否和现有环境冲突！！！！！！！！！--------------");
            throw new BizException("-------------检查配置文件active字段是否和现有环境冲突！！！！！！！！！--------------");
        }

        HashMap<String, String> valuesMap = new HashMap<>();
        valuesMap.put("appName", namespace);
        valuesMap.put("serviceName", cceDto.getName());
        valuesMap.put("swrTag", cceDto.getImage());
        valuesMap.put("profile", ACTIVE);
        valuesMap.put("discoveryAddress", cseInfo.get("discoveryAddress"));
        valuesMap.put("configAddress", cseInfo.get("configAddress"));
        valuesMap.put("accessKey", iamInfo.get("accessKey"));
        valuesMap.put("secretKey", sk);
        valuesMap.put("cseName", cseInfo.get("cseName"));
        valuesMap.put("csePassword", pwd);
        valuesMap.put("projectId", iamInfo.get("projectId"));
        valuesMap.put("cseProject", cseInfo.get("cseProject"));

        Boolean enabled = ServiceManageFrameTypeEnum.NACOS.getName().equals(CONFIG_DISCOVERY_FRAME);
        valuesMap.put("nacosEnabled", String.valueOf(enabled));
        valuesMap.put("cseEnabled", String.valueOf(!enabled));
        if (enabled) {
            valuesMap.put("nacosNamespace", nacos.getNamespace());
            valuesMap.put("nacosGroup", namespace);
        }

        valuesMap.put("swaggerEnabled", String.valueOf(swaggerEnabled));
        return valuesMap;
    }

    /**
     * 解析 创建 cce deployment 的请求结果
     */
    private void parseCreateDeploymentResult(CceDeploymentResponse deploymentResponse, CceDto cceDto) {
        if (deploymentResponse == null) {
            log.error("!!!!!!!!!! 创建 deployment {}失败 !!!!!!!!!!", cceDto.getName());
            throw new BizException(ExceptionCode.DEPLOYMENT_CREATE_FAILED);
        } else {
            log.info("========== end: 创建 cce deployment : {} ==========", cceDto.getName());
            createService(cceDto);

            // 创建 Nginx 相关资源
            if (ComponentLabelEnum.GATEWAY.getLabel().equals(cceDto.getLabel())) {
                createNginxResource(cceDto);
            }
        }
    }

    /**
     * 创建 cce service
     */
    private void createService(CceDto cceDto) {
        log.info("========== start: 创建 cce service : {} ==========", cceDto.getName());

        // 这里不进行网关clusterIP的创建，下面会进行创建
        if (ComponentLabelEnum.GATEWAY.getLabel().equals(cceDto.getLabel())) {
            return;
        }
        // 获取平台侧租户code作为namespace名
        String namespace = tenantBaseMapper.getIsvTenantCode();

        String name = CceServicePrefixEnum.CLUSTER_IP.getPrefix().concat(cceDto.getName());
        int port = cceDto.getPort();
        CceServiceCreateRequest requestBody =
            CceRequestBody.createClusterIPServiceBody(name, cceDto.getName(), port, cceDto.getPort());
        // 请求结果
        createAndSaveCceService(cceDto, namespace, requestBody, name, port, null);
    }

    /**
     * 创建CCE
     * Service
     */
    private CceServiceResponse createCceService(CceDto cceDto, String namespaceName,
        CceServiceCreateRequest requestBody, String name) {
        CceServiceResponse serviceResponse =
            CceServiceApi.create(endpoint.getCce(), cce.getClusterId(), namespaceName, requestBody, getHeaders());

        // 成功创建cce资源后，创建前端ingress默认路由规则
        if (ComponentLabelEnum.B_UI.getLabel().equals(cceDto.getLabel())) {
            // 获取平台侧租户code作为namespace名
            String namespace = tenantBaseMapper.getIsvTenantCode();

            // 初始化ingress
            initNginxIngress(namespace, cceDto, name);
        }
        log.info("========== end: 创建 cce service : {} ==========", cceDto.getName());
        return serviceResponse;
    }

    /**
     * 初始化ingress
     */
    private void initNginxIngress(String namespace, CceDto cceDto, String serviceName) {
        AppTemplate saasAppInfo = appTemplateBaseMapper.getSaasAppInfo();
        String contextPath = componentInfoMapper.selectBUIContextPathByAppId(saasAppInfo.getId());
        IngressInfo ingressInfo = IngressRequestBody.initIngress(IngressCreatDTO.builder()
            .port(cceDto.getPort())
            .cceClusterName(cce.getClusterName())
            .serviceName(serviceName)
            .host(DomainNameUtil.getDomainName())
            .ingressClassName(ingressClassName)
            .cceVersion(cce.getClusterVersion())
            .isInitIngress(true)
            .accessPrefix(contextPath)
            .build());

        NginxIngressApi.create(cce.getClusterId(), endpoint.getCce(), namespace, getHeaders(), ingressInfo,
            cce.getClusterVersion());
    }

    /**
     * 创建并保存CCE
     * Service
     *
     * @param cceDto
     *        CCE基本信息
     * @param namespaceName
     *        命名空间名称
     * @param requestBody
     *        请求体
     * @param name
     *        Service名称
     * @param port
     *        服务端口
     * @param elbInfoId
     *        ELB记录ID
     */
    private void createAndSaveCceService(CceDto cceDto, String namespaceName, CceServiceCreateRequest requestBody,
        String name, int port, String elbInfoId) {
        // 创建CCE Service
        CceServiceResponse result = createCceService(cceDto, namespaceName, requestBody, name);
        // 保存CCE相关资源信息
        saveCceResource(name, port, result, cceDto, elbInfoId);
    }

    /**
     * 创建 Nginx 相关资源
     */
    private void createNginxResource(CceDto cceDto) {
        // 获取平台侧租户code作为namespace名
        String namespace = tenantBaseMapper.getIsvTenantCode();

        // 创建网关集群内service
        String name = CceServicePrefixEnum.CLUSTER_IP.getPrefix().concat(cceDto.getName());

        CceServiceCreateRequest serviceCreateRequest =
            CceRequestBody.createClusterIPServiceBody(name, cceDto.getName(), cceDto.getPort(), cceDto.getPort());
        CceServiceResponse serviceResponse =
            CceServiceApi.create(endpoint.getCce(), cce.getClusterId(), namespace, serviceCreateRequest, getHeaders());
        // 存储cce service信息
        saveCceResource(name, cceDto.getPort(), serviceResponse, cceDto, null);
        String clusterIP = serviceResponse.getSpec().getClusterIP();

        String protocol = UrlConstant.HTTP_PREFIX;
        String gatewayHost = protocol + clusterIP + ":" + cceDto.getPort();
        // 将网关host保存到redis中，使用hash存储，key为租户编码，value为网关host key为域名 value为tenantcode
        redisUtils.hset(RedisKeyEnum.SAASLANDING_GATEWAY_HOST.getKey(), namespace, gatewayHost);
        redisUtils.hset(RedisKeyEnum.SAASLANDING_DOMAIN_TENANT.getKey(), DomainNameUtil.getDomainName(), namespace);
        log.info("redis插入域名对应tenant");
        // 创建 CCE Nginx 配置项
        createCceNginxConfig(gatewayHost, namespace);
    }

    /**
     * 创建 CCE Nginx 配置项
     */
    private void createCceNginxConfig(String gatewayHost, String namespace) {
        // 根据是否开启Https，创建不同的nginx configMap
        Map<String, Object> valuesMap = generateNginxConfigMapValue(gatewayHost);
        AppTemplate saasAppInfo = appTemplateBaseMapper.getSaasAppInfo();
        String contextPath = componentInfoMapper.selectBUIContextPathByAppId(saasAppInfo.getId());
        // 现在https的策略是只在elb的南向做https，所以下面nginx的配置使用http的配置
        CceConfigMapCreateRequest requestBody =
            CceRequestBodyUtil.createUIConfigMapBody(valuesMap, ServerType.NEW_UI.getComponentName(), contextPath);
        CceConfigMapApi.create(endpoint.getCce(), cce.getClusterId(), namespace, requestBody, getHeaders());
        log.info("创建 CCE配置项 成功");
    }

    private Map<String, Object> generateNginxConfigMapValue(String gatewayHost) {
        Map<String, Object> valuesMap = new HashMap<>();
        valuesMap.put(EnvConstant.PLATFORM_GATEWAY_HOST, gatewayHost);
        // 暂时skywalking.enabled只能为true，下个迭代实现或去除skywalking的开关
        if (skywalking.isEnabled()) {
            valuesMap.put("skyWalkingUiAndNginxReportHost", skywalking.getUiReportHost());
        }
        return valuesMap;
    }

    /**
     * 保存 CCE 资源信息
     */
    private void saveCceResource(String name, int port, CceServiceResponse result, CceDto cceDto, String elbInfoId) {
        log.info("========== start: 保存 cce 资源信息 : {} ==========", cceDto.getName());
        String type = result.getSpec().getType();
        String clusterIP = result.getSpec().getClusterIP();

        // 查找平台侧控制台应用 应用模板表id
        Long appId = appTemplateBaseMapper.selectIdByType(1);

        // 查找 ISV tenant code
        String tenantCode = tenantBaseMapper.getIsvTenantCode();

        // hsl_cce_resource
        CceResource cceResource = new CceResource();
        cceResource.setAppId(appId)
            .setTenantCode(tenantCode)
            .setComponentId(cceDto.getComponentId())
            .setNamespace(tenantCode)
            .setCurrentVersion(cceDto.getComponentVersion())
            .setCurrentDeployment(cceDto.getName())
            .setCurrentImageId(cceDto.getId())
            .setIsUpgrading(false);

        // 暂时保存的service信息
        cceResource.setServiceName(name).setServiceIp(clusterIP).setServicePort(port);

        cceResourceBaseMapper.insert(cceResource);
        Long cceResourceId = cceResource.getId();

        // hsl_cce_service_resource
        CceServiceResource cceServicResource = new CceServiceResource();
        cceServicResource.setCceResourceId(cceResourceId)
            .setName(name)
            .setType(type)
            .setPort(port)
            .setTargetPort(cceDto.getPort())
            .setClusterIp(clusterIP);

        if ("LoadBalancer".equals(type)) {
            String loadBalancerIP = result.getSpec().getLoadBalancerIP();
            cceServicResource.setLoadbalancerIp(loadBalancerIP).setElbInfoId(Long.valueOf(elbInfoId));
        } else if ("NodePort".equals(type)) {
            cceServicResource.setElbInfoId(Long.valueOf(elbInfoId));
        }

        cceServiceResourceBaseMapper.insert(cceServicResource);

        // hsl_cce_deployment_resource 保存平台侧部署deployment信息
        CceDeploymentResource cceDeploymentResource = new CceDeploymentResource();
        cceDeploymentResource.setAppId(appId)
            .setComponentId(cceDto.getComponentId())
            .setNamespace(tenantCode)
            .setDeployment(cceDto.getName())
            .setVersion(cceDto.getComponentVersion())
            .setImageId(cceDto.getId())
            .setRouteLinkKey(RouteLinkConstant.ROUTE_LINK_KEY_BASE);
        cceDeploymentResourceBaseMapper.insert(cceDeploymentResource);

        // hsl_cce_deployment_resource_relation 保存关系-平台侧
        CceDeploymentResourceRelation cceDeploymentResourceRelation = new CceDeploymentResourceRelation();
        cceDeploymentResourceRelation.setCceResourceId(cceResourceId)
            .setCceDeploymentResourceId(cceDeploymentResource.getId());
        cceDeploymentResourceRelationBaseMapper.insert(cceDeploymentResourceRelation);

        log.info("========== end: 保存 cce 资源信息 : {} ==========", cceDto.getName());
    }

    /**
     * 获取平台侧加密口令
     *
     * @return 平台侧加密口令
     */
    private String getIsvSalt() {
        SaltDto saltDto = workSecretKeyMapper.getPlatformSaltDto();
        return EncryptionUtil.decrypt(saltDto.getPwdSalt(), saltDto.getPassword());
    }

    private Map<String, String> getCseInfo() {
        Map<String, String> map = new HashMap<>();
        if (ServiceManageFrameTypeEnum.CSE.getName().equals(CONFIG_DISCOVERY_FRAME)) {
            map.put("discoveryAddress", cse.getDiscoveryAddress());
            map.put("configAddress", cse.getConfigAddress());
            map.put("cseName", cse.getName());
            map.put("csePwd", cse.getPassword());
            map.put("cseProject", cse.getCseProjectId());
        }
        if (ServiceManageFrameTypeEnum.NACOS.getName().equals(CONFIG_DISCOVERY_FRAME)) {
            String address = nacos.getNacosServers().get(0);
            map.put("configAddress", address);
            map.put("discoveryAddress", address);
            map.put("cseName", nacos.getName());
            map.put("csePwd", nacos.getPassword());
            map.put("cseProject", "default");
        }

        return map;
    }

    private Map<String, String> getIamInfo() {
        Map<String, String> map = new HashMap<>();
        map.put("secretKey", iam.getSecretKey());
        map.put("accessKey", iam.getAccessKey());
        map.put("projectId", iam.getProjectId());
        return map;
    }

    /**
     * 获取请求头
     */
    private Map<String, String> getHeaders() {
        if (headers == null) {
            headers = CceHeaderUtil.getHeaders(iam.getEndpoint(), iam.getIamDomain(), iam.getIamUser(),
                iam.getIamPassword(), iam.getProjectId());
        }
        return headers;
    }
}
